﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WeddingEventManagement_API
{
    public class LocationManager : ILocationManager
    {
        #region Read

        public Location GetLocation(int id)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                Location location = db.Locations.Include("LocationPhotos").Single(l => l.LocationID == id);
                location.LastViewed = DateTime.UtcNow;
                if (!location.NumberOfViews.HasValue)
                {
                    location.NumberOfViews = 1;
                }
                else
                {
                    location.NumberOfViews++;
                }
                db.SaveChanges();
                
                return location;
            }
        }

        public List<Location> GetLocations(string filter = null)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                if (filter == null)
                {
                    return db.Locations.ToList();
                }
                return db.Locations.Where(i => i.Name.Contains(filter)).ToList();
            }
        }


        public List<Location> GetRecentlyViewedLocations(int num)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                return db.Locations.Where(i => i.LastViewed.HasValue).OrderByDescending(i => i.LastViewed).Take(num).ToList();
            }
        }

        public List<LocationOccurrence> GetMostUsedLocations(int num)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                List<LocationOccurrence> locationOccurrenceList = new List<LocationOccurrence>();

                IEnumerable<IGrouping<int,int>> elGroups = db.EventLocations.GroupBy(i => i.LocationID, i => i.LocationID);
                var ordered = elGroups.OrderByDescending(i => i.ToList().Count).Take(num);
                foreach (IGrouping<int,int> elGroup in ordered)
                {
                    locationOccurrenceList.Add(new LocationOccurrence { Location = db.Locations.FirstOrDefault(i => i.LocationID == elGroup.Key), OccuranceNum = elGroup.ToList().Count });
                }

                return locationOccurrenceList;
            }
        }

        public List<Location> GetMostViewedLocations(int num)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                return db.Locations.Where(i => i.NumberOfViews.HasValue).OrderByDescending(i => i.NumberOfViews).Take(num).ToList();
            }
        }

        public List<string> GetUniqueLocationTypes()
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                var types = (from c in db.EventLocations where !String.IsNullOrEmpty(c.LocationType) select c.LocationType).Distinct();
                return types.ToList();
            }
        }

        public List<string> GetUniqueLocationVenueTypes()
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                var types = (from c in db.Locations where !String.IsNullOrEmpty(c.Type) select c.Type).Distinct();
                return types.ToList();
            }
        }

        #endregion

        #region Create, Update, Delete

        public void DeleteLocation(int locationID)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                Location location = db.Locations.FirstOrDefault(i => i.LocationID == locationID);
                db.Locations.Remove(location);
                db.SaveChanges();
            }
        }

        public void UpdateLocation(Location location, Guid? updateUserID = null)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                location.UpdatedUserID = updateUserID;
                db.Locations.Attach(location);
                db.Entry(location).State = System.Data.EntityState.Modified;
                db.SaveChanges();
            }
        }

        public int CreateLocation(string name, string streetAddress, string city, string postalCode, Province province,
            string lat, string lng, string type, int capacity,
            string contactName = null, string contactPhone = null, string contactEmail = null, string notes = null,
            Guid? creatorUserID = null)
        {
            CheckIfLocationExists(name, streetAddress, city, postalCode, province);

            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                DateTime now = DateTime.UtcNow;
                Location location = new Location
                {
                    City = city,
                    Name = name,
                    Notes = notes,
                    PostalCode = postalCode,
                    Province = province.ToString(),
                    StreetAddress = streetAddress,
                    ContactName = contactName,
                    ContactEmail = contactEmail,
                    ContactPhone = contactPhone,
                    CreatedDate = now,
                    CreatorUserID = creatorUserID,
                    Lat = lat,
                    Lng = lng,
                    Type = type,
                    Capacity = capacity
                };
                db.Locations.Add(location);
                db.SaveChanges();

                string sProv = province.ToString();
                Location newLocation = db.Locations.FirstOrDefault(i => i.Name == name
                    && i.City == city
                    && i.PostalCode == postalCode
                    && i.Province == sProv
                    && i.StreetAddress == streetAddress
                );

                return newLocation.LocationID;
            }
        }

        #endregion

        #region Validation

        private void CheckIfLocationExists(string name, string streetAddress, string city, string postalCode, Province province)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                var strProv = province.ToString();
                Location location = db.Locations.FirstOrDefault(i => i.Name == name
                    && i.StreetAddress == streetAddress
                    && i.City == city
                    && i.PostalCode == postalCode
                    && i.Province == strProv
                );

                if (location != null)
                {
                    throw new LocationExistsException(String.Format("A location with Name: {0}, Address: {1}, City: {2}, Postal Code: {3}, and Province {4} already exists in the database",
                        new object[] { name, streetAddress, city, postalCode, province.ToString() }), location);
                }
            }
        }

        #endregion

        public void AddPhoto(LocationPhoto locationPhoto)
        {
            using (CreativeSystemsEntities db = new CreativeSystemsEntities())
            {
                // If currently no photos
                if (db.Locations.FirstOrDefault(i => i.LocationID == locationPhoto.LocationID).LocationPhotos.Count() < 1)
                {
                    // Make it featured
                    locationPhoto.Featured = true;
                }
                else
                {
                    // Don't make it featured
                    locationPhoto.Featured = false;
                }

                db.LocationPhotoes.Add(locationPhoto);
                db.SaveChanges();
            }
        }
    }
}
